from typing import List

from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session

from sqlalchemy import select, and_
from typing import Optional
from models.medicine_model import Medicine
from models.medicine_record_model import RecordOfMedicine
from models.payment_model import Payment
from models.prescription_model import Prescription
from models.user_models import Doctor, Patient
from schemas.payment_schemas import PaymentType, PaymentStatus
from schemas.prescription_schemas import PrescriptionCreate, PrescriptionUpdate, PrescriptionWithPaymentStatusResponse, \
    MedicineItemWithName
from fastapi import HTTPException

from services.datetime_service import now_time
from services.payment_service import get_or_create_payment


def get_prescription(db: Session, prescription_id: int):
    db_prescription = db.query(Prescription).filter(Prescription.ID == prescription_id).first()
    if not db_prescription:
        raise HTTPException(status_code=404, detail="Prescription not found")
    return db_prescription

def get_prescriptions(db: Session, skip: int = 0, limit: int = 100):
    return db.query(Prescription).offset(skip).limit(limit).all()

def get_paid_prescriptions(db: Session) -> List[PrescriptionWithPaymentStatusResponse]:
    # 查询所有处方和对应患者、医生、支付信息
    prescriptions = db.query(Prescription).all()

    # 查询支付信息，缓存映射 prescription.ID -> Payment
    payments_map = {
        p.ref_ID: p
        for p in db.query(Payment).filter(Payment.pay_y_n == 'Y').all()
        if p.ref_ID is not None
    }

    result = []

    for pre in prescriptions:
        # 关联医生和患者
        doctor = db.query(Doctor).filter(Doctor.ID == pre.doctor_ID).first()
        patient = db.query(Patient).filter(Patient.ID == pre.patient_ID).first()

        # 解析处方的 medicines JSON 字段，补充药品名
        medicine_items_with_name = []
        for med_item in pre.medicines:  # medicines 是一个 List[dict]
            med_id = med_item.get("ID")
            dose = med_item.get("dose")
            frequency = med_item.get("frequency")

            medicine = db.query(Medicine).filter(Medicine.ID == med_id).first()
            if medicine:
                medicine_items_with_name.append(
                    MedicineItemWithName(
                        ID=med_id,
                        dose=dose,
                        frequency=frequency,
                        medicine_name=medicine.medicine_name
                    )
                )

        # 判断是否支付
        payment = payments_map.get(pre.ID)
        is_paid = payment is not None

        pre_response = PrescriptionWithPaymentStatusResponse(
            ID=pre.ID,
            doctor_ID=pre.doctor_ID,
            patient_ID=pre.patient_ID,
            pre_date=pre.pre_date,
            medicines=medicine_items_with_name,
            is_paid=is_paid,
            payment_id=payment.ID if is_paid else None,
            # 增加完整的医生、患者信息
            doctor=doctor.to_dict() if doctor else None,
            patient=patient.to_dict() if patient else None,
            is_done=pre.is_done,
        )
        result.append(pre_response)

    return result

def get_prescriptions_with_payment_status(
    db: Session,
    patient_id: Optional[int] = None
) -> List[PrescriptionWithPaymentStatusResponse]:
    # Step 1: 查询所有处方和对应支付信息
    stmt = (
        select(
            Prescription,
            Payment.ID.label("payment_id"),
            Payment.pay_y_n
        )
        .outerjoin(Payment, and_(
            Payment.ref_ID == Prescription.ID,
            Payment.type == 'MEDI'
        ))
    )

    if patient_id is not None:
        stmt = stmt.where(Prescription.patient_ID == patient_id)

    stmt = stmt.order_by(Prescription.pre_date.desc())
    results = db.execute(stmt).all()

    # Step 2: 提取所有涉及到的 medicine_id
    medicine_id_set = set()
    for pres, _, _ in results:
        for med in pres.medicines:
            medicine_id_set.add(med['ID'])

    # Step 3: 一次性查询所有涉及的 medicine 对象
    medicines_query = db.execute(
        select(Medicine.ID, Medicine.medicine_name).where(Medicine.ID.in_(medicine_id_set))
    ).all()
    medicine_name_dict = {mid: name for mid, name in medicines_query}

    # Step 4: 构建响应对象
    response_list = []
    for pres, payment_id, pay_y_n in results:
        is_paid = (pay_y_n == 'Y') if pay_y_n else False

        medicine_items_with_name = []
        for med in pres.medicines:
            medicine_items_with_name.append(
                MedicineItemWithName(
                    ID=med['ID'],
                    dose=med['dose'],
                    frequency=med['frequency'],
                    medicine_name=medicine_name_dict.get(med['ID'], "未知药品")
                )
            )

        response_list.append(
            PrescriptionWithPaymentStatusResponse(
                ID=pres.ID,
                doctor_ID=pres.doctor_ID,
                patient_ID=pres.patient_ID,
                pre_date=pres.pre_date,
                medicines=medicine_items_with_name,
                is_paid=is_paid,
                payment_id=payment_id,
                is_done=pres.is_done,
            )
        )

    return response_list


def get_unpaid_prescriptions_by_patient(db: Session, patient_id: int):
    """
    获取某个患者所有未支付的处方。
    """
    stmt = (
        select(Prescription)
        .join(Payment, Payment.ref_ID == Prescription.ID)
        .where(
            Payment.type == PaymentType.MEDI,
            Payment.pay_y_n == PaymentStatus.N,
            Prescription.patient_ID == patient_id
        )
        .order_by(Prescription.pre_date.desc())
    )
    return db.scalars(stmt).all()



def create_prescription(db: Session, prescription: PrescriptionCreate, is_doctor: bool):
    try:

        if is_doctor:
            print("is_doctor")
        else:
            print("is_not_doctor")

        # 创建处方记录
        db_prescription = Prescription(
            doctor_ID=prescription.doctor_ID,
            patient_ID=prescription.patient_ID,
            pre_date=prescription.pre_date,
            medicines=[item.model_dump() for item in prescription.medicines],
            is_done=prescription.is_done,
        )
        db.add(db_prescription)
        db.flush()  # 获取 ID 但不提交

        # 如果是医生创建，添加支付记录
        if is_doctor:
            total_expense = 0
            for item in prescription.medicines:
                medicine = db.query(Medicine).filter(Medicine.ID == item.ID).first()
                if not medicine:
                    raise HTTPException(status_code=404, detail=f"ID为{item.ID}的药品不存在")
                if medicine.price is None:
                    raise HTTPException(status_code=400, detail=f"药品 {medicine.medicine_name} 没有设置价格")
                total_expense += medicine.price * item.dose

            db_payment = Payment(
                patient_ID=prescription.patient_ID,
                con_date=prescription.pre_date,
                expense=total_expense,
                type=PaymentType.MEDI,
                pay_y_n=PaymentStatus.N,
                ref_ID=db_prescription.ID,
            )
            db.add(db_payment)
            db.flush()  # 强制推送到数据库，但事务未提交
            # # 立刻查询确认
            # inserted_payment = db.query(Payment).filter(Payment.ref_ID == db_prescription.ID).first()
            # print(f"Inserted payment after flush: {inserted_payment}")

        db.commit()

        db.refresh(db_prescription)
        return db_prescription

    except SQLAlchemyError as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"创建失败: {str(e)}")

def update_prescription(db: Session, prescription_id: int, prescription: PrescriptionUpdate):
    db_prescription = get_prescription(db, prescription_id)
    update_data = prescription.dict(exclude_unset=True)
    for key, value in update_data.items():
        setattr(db_prescription, key, value)
    db.add(db_prescription)
    db.commit()
    db.refresh(db_prescription)
    return db_prescription

def delete_prescription(db: Session, prescription_id: int):
    db_prescription = get_prescription(db, prescription_id)
    db.delete(db_prescription)
    db.commit()
    return {"message": "Prescription deleted successfully"}

def get_prescriptions_by_patient(db: Session, patient_id: int):
    return db.query(Prescription).filter(Prescription.patient_ID == patient_id).all()


def pharmacist_deal_prescription(db: Session, prescription_id: int, pharmacist_id: int):
    """
    药剂师处理处方。将处方标记为已完成，并创建对应的若干条发药记录。
    """
    try:
        db.begin()

        # 1. 查找处方
        prescription = db.query(Prescription).filter(Prescription.ID == prescription_id).first()
        if not prescription:
            raise HTTPException(status_code=404, detail="处方不存在")

        # 检查处方是否已经处理
        if prescription.is_done == 'Y':
            raise HTTPException(status_code=400, detail="该处方已被处理过")

        # 2. 将处方标记为已完成
        prescription.is_done = 'Y'

        # 3. 为处方中的每种药品创建发药记录
        if prescription.medicines:
            for medicine_item in prescription.medicines:
                medicine_id = medicine_item.get("ID")
                dose = medicine_item.get("dose")  # 假设dose就是发药数量

                if not medicine_id or not dose:
                    raise HTTPException(status_code=400, detail="处方药品信息不完整，缺少ID或剂量")

                # 查找药品信息
                medicine = db.query(Medicine).filter(Medicine.ID == medicine_id).first()
                if not medicine:
                    raise HTTPException(status_code=404, detail=f"药品ID为 {medicine_id} 的药品不存在")

                # 检查库存
                # 注意：这里使用 medicine.medicine_name，而非 medicine.name
                if medicine.stock < dose:
                    raise HTTPException(status_code=400,
                                        detail=f"药品 {medicine.medicine_name} 库存不足，需要 {dose}，当前库存 {medicine.stock}")

                # 计算总价
                # 根据 Medicine 模型，单价字段是 'price'
                total_price = dose * medicine.price if hasattr(medicine, 'price') else 0

                # 创建发药记录
                db_record = RecordOfMedicine(
                    medicine_ID=medicine_id,
                    medicine_name=medicine.medicine_name, # 填充 medicine_name 字段
                    patient_ID=prescription.patient_ID,
                    phar_ID=pharmacist_id, # 对应 RecordOfMedicine 中的 phar_ID
                    number=dose,
                    total_price=total_price,
                    con_date=prescription.pre_date,  # 使用处方日期作为咨询日期
                    dispensed_at=now_time().utcnow(), # 填充 dispensed_at 字段，使用当前UTC时间
                    # prescription_id=prescription.ID # 如果RecordOfMedicine模型有prescription_id字段，可以添加
                )
                db.add(db_record)

                # 更新药品库存
                medicine.stock -= dose

                # 4. 查找或创建支付记录并更新药品费用 (根据您的注释，这部分目前被注释掉了，但如果需要，请取消注释并确保 get_or_create_payment 函数可用)
                # payment = get_or_create_payment(db, prescription.patient_ID, prescription.pre_date)
                # payment.medi_expense += total_price
                # payment.total_expense = payment.con_expense + payment.medi_expense
        else:
            raise HTTPException(status_code=400, detail="处方中没有药品信息")

        db.commit()
        db.refresh(prescription)  # 刷新处方对象以获取最新状态

        return prescription

    except HTTPException as he:
        db.rollback()
        raise he
    except Exception as e:
        db.rollback()
        raise HTTPException(
            status_code=500,
            detail=f"处理处方失败: {str(e)}"
        )

